package utils

/**

定义了一个缓存池，用来避免频繁的内存申请

*/

import (
	"bytes"
	"container/list"
	"errors"
	"shell/global"
	"sync"
)

var (
	ErrBufNotBelongPool = errors.New("error buff is not belong to this pool")
	ErrUnknown          = errors.New("error unknown about buff pool")
)

type Buff struct {
	b       []byte
	isUsing bool
	n       int
	pool    *BuffPool
}

func (b *Buff) Reset() {
	b.n = 0
	b.isUsing = false
	for i := range b.b {
		b.b[i] = 0
	}
}

func (b *Buff) GetB() []byte {
	return b.b
}

func (b *Buff) GetN() int {
	return b.n
}

func (b *Buff) SetN(n int) *Buff {
	b.n = n
	return b
}

func NewBuff(size int, pool *BuffPool) *Buff {
	return &Buff{
		b:       make([]byte, size),
		isUsing: false,
		n:       0,
		pool:    pool,
	}
}

type BuffPool struct {
	buffSize   int                     // 每个缓存块的大小
	l          *list.List              // 缓存链表
	lock       sync.Mutex              // 读写锁，用于保护链表的读写操作
	index      *list.Element           // 指向链表元素的指针，应该指向一个下一个GetBuff返回的缓存块，如果为null，表示链表需要扩容
	m          map[*Buff]*list.Element // map，用于根据Buff检索链表元素
	usingBuffs int                     // 借出的内存块数量
}

func NewBuffPool(size int) *BuffPool {
	result := &BuffPool{}
	result.buffSize = size
	result.l = list.New()
	result.lock = sync.Mutex{}
	result.index = nil
	result.m = make(map[*Buff]*list.Element)
	result.usingBuffs = 0
	return result
}

// 获取一个内存块
func (bp *BuffPool) GetBuff() *Buff {
	bp.lock.Lock()
	defer bp.lock.Unlock()
	bp.usingBuffs++
	if bp.index == nil {
		// 需要申请新内存块
		buf := NewBuff(bp.buffSize, bp)
		buf.isUsing = true
		bp.l.PushBack(buf)
		bp.m[buf] = bp.l.Back()
		return buf
	} else {
		// 不需要申请新内存块
		next := bp.index.Next()
		if next != nil {
			defer func() {
				bp.index = next
			}()
		} else {
			defer func() {
				bp.index = nil
			}()
		}
		buf := bp.index.Value.(*Buff)
		buf.isUsing = true
		bp.l.MoveToFront(bp.index)
		return buf
	}
}

func (bp *BuffPool) PutBuff(buf *Buff) error {
	if buf == nil {
		return global.ErrNil
	}
	if buf.pool != bp {
		return ErrBufNotBelongPool
	}
	bp.lock.Lock()
	defer bp.lock.Unlock()
	buf.Reset()
	element := bp.m[buf]
	if element == nil {
		return ErrUnknown
	}
	bp.l.MoveToBack(element)
	if bp.index == nil {
		// 归还到列表尾部
		bp.index = element
	}
	bp.usingBuffs--
	return nil
}

func (bp *BuffPool) BuffNum() int {
	return bp.l.Len()
}

func (bp *BuffPool) UsingBuffNum() int {
	return bp.usingBuffs
}

func (bp *BuffPool) BuffSize() int {
	return bp.buffSize
}

type BytesPool struct {
	Pool sync.Pool
}

func NewBytesPool() *BytesPool {
	return &BytesPool{
		Pool: sync.Pool{
			New: func() any {
				return new(bytes.Buffer)
			},
		},
	}
}

func (pb *BytesPool) Get() *bytes.Buffer {
	return pb.Pool.Get().(*bytes.Buffer)
}

func (pb *BytesPool) Put(b *bytes.Buffer) {
	b.Reset()
	pb.Pool.Put(b)
}
